/**
 * Copyright (C) 2021-2022 maminjie <canpool@163.com>
 * SPDX-License-Identifier: LGPL-2.1
 * Powered by Qt-Advanced-Docking-System
**/

#include "dockcontainer.h"

#include "dockmanager_p.h"
#include "dockpannel.h"
#include "dockstatereader.h"
#include "dockmanager.h"
#include "dockoverlay.h"
#include "docksplitter.h"
#include "dockwidget.h"
#include "dockfloatingwidget.h"

#include <QAbstractButton>
#include <QDebug>
#include <QEvent>
#include <QGridLayout>
#include <QList>
#include <QLoggingCategory>
#include <QPointer>
#include <QVariant>
#include <QXmlStreamWriter>

#include <functional>
#include <iostream>

QADS_BEGIN_NAMESPACE

static unsigned int zOrderCounter = 0;

enum DropMode {
    DropModeIntoPannel,    ///< drop widget into a dock pannel
    DropModeIntoContainer, ///< drop into container
    DropModeInvalid        ///< invalid mode - do not drop
};

/**
 * Converts dock area ID to an index for array access
 */
static int areaToIndex(DockWidgetArea area)
{
    switch (area) {
    case LeftDockWidgetArea:
        return 0;
    case RightDockWidgetArea:
        return 1;
    case TopDockWidgetArea:
        return 2;
    case BottomDockWidgetArea:
        return 3;
    case CenterDockWidgetArea:
        return 4;
    default:
        return 4;
    }
}

/**
 * Helper function to ease insertion of dock area into splitter
 */
static void insertWidgetIntoSplitter(QSplitter *splitter, QWidget *widget, bool append)
{
    if (append) {
        splitter->addWidget(widget);
    } else {
        splitter->insertWidget(0, widget);
    }
}

class DockInsertParam : public QPair<Qt::Orientation, bool>
{
public:
    DockInsertParam(Qt::Orientation orientation, bool append)
        : QPair<Qt::Orientation, bool>(orientation, append)
    {}

    Qt::Orientation orientation() const {
        return this->first;
    }
    bool append() const {
        return this->second;
    }
    int insertOffset() const {
        return append() ? 1 : 0;
    }
};

/**
 * Private data class of DockContainer class (pimpl)
 */
class DockContainerPrivate
{
public:
    DockContainerPrivate(DockContainer *parent);

    DockPannel *addDockWidget(DockWidgetArea area, DockWidget *dockWidget);
    DockPannel *addDockWidget(DockWidgetArea area, DockWidget *dockWidget, DockPannel *targetPannel);

    void addDockPannel(DockPannel *newPannel, DockWidgetArea area = CenterDockWidgetArea);

    void dropIntoContainer(DockFloatingWidget *floatingWidget, DockWidgetArea area);
    void dropIntoSection(DockFloatingWidget *floatingWidget, DockPannel *targetPannel, DockWidgetArea area);

    void moveToNewSection(QWidget *widget, DockPannel *targetPannel, DockWidgetArea area);
    void moveToContainer(QWidget *widget, DockWidgetArea area);

    void dropIntoCenterOfSection(DockFloatingWidget *floatingWidget, DockPannel *targetPannel);
    void moveIntoCenterOfSection(QWidget *widget, DockPannel *targetPannel);

    void addDockPannels(const QList<DockPannel *> newDockPannels);
    void appendDockPannels(const QList<DockPannel *> newDockPannels);

    void saveChildNodesState(QXmlStreamWriter &stream, QWidget *widget);

    bool restoreChildNodes(DockStateReader &stateReader, QWidget *&createdWidget, bool testing);
    bool restoreSplitter(DockStateReader &stateReader, QWidget *&createdWidget, bool testing);
    bool restoreDockPannel(DockStateReader &stateReader, QWidget *&createdWidget, bool testing);

    void dumpRecursive(int level, QWidget *widget) const;

    DropMode getDropMode(const QPoint &targetPosition);

    DockInsertParam dockAreaInsertParameters(DockWidgetArea area);

    void initVisibleDockPannelCount();

    int visibleDockPannelCount();
    void onVisibleDockPannelCountChanged();
    void onDockPannelViewToggled(bool visible);

    void emitDockPannelsRemoved();
    void emitDockPannelsAdded();

    DockSplitter *createSplitter(Qt::Orientation orientation, QWidget *parent = nullptr);

    void adjustSplitterSizesOnInsertion(QSplitter *splitter, qreal lastRatio = 1.0);

    void updateSplitterHandles(QSplitter* splitter);

    bool widgetResizesWithContainer(QWidget* widget);

public:
    DockContainer *q;
    QPointer<DockManager> m_dockManager;
    QGridLayout *m_layout = nullptr;
    QSplitter *m_rootSplitter = nullptr;
    QList<DockPannel *> m_dockPannels;
    DockPannel *m_lastAddedPannelCache[5];
    DockPannel *m_topLevelDockPannel = nullptr;
    unsigned int m_zOrderIndex = 0;
    bool m_isFloating = false;
    int m_visibleDockPannelCount = -1;
}; // DockContainerPrivate

DockContainerPrivate::DockContainerPrivate(DockContainer *parent)
    : q(parent)
{
    std::fill(std::begin(m_lastAddedPannelCache), std::end(m_lastAddedPannelCache), nullptr);
}

DropMode DockContainerPrivate::getDropMode(const QPoint &targetPosition)
{
    DockPannel *pannel = q->dockPannelAt(targetPosition);
    auto dropArea = InvalidDockWidgetArea;
    auto containerDropArea = m_dockManager->containerOverlay()->dropAreaUnderCursor();

    if (pannel) {
        auto dropOverlay = m_dockManager->pannelOverlay();
        dropOverlay->setAllowedAreas(pannel->allowedAreas());
        dropArea = dropOverlay->showOverlay(pannel);

        if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) {
            dropArea = InvalidDockWidgetArea;
        }

        if (dropArea != InvalidDockWidgetArea) {
            return DropModeIntoPannel;
        }
    }

    // mouse is over container
    if (InvalidDockWidgetArea == dropArea) {
        dropArea = containerDropArea;
        if (dropArea != InvalidDockWidgetArea) {
            return DropModeIntoContainer;
        }
    }

    return DropModeInvalid;
}

DockInsertParam DockContainerPrivate::dockAreaInsertParameters(DockWidgetArea area)
{
    switch (area) {
    case TopDockWidgetArea:
        return DockInsertParam(Qt::Vertical, false);
    case RightDockWidgetArea:
        return DockInsertParam(Qt::Horizontal, true);
    case CenterDockWidgetArea:
    case BottomDockWidgetArea:
        return DockInsertParam(Qt::Vertical, true);
    case LeftDockWidgetArea:
        return DockInsertParam(Qt::Horizontal, false);
    default:
        return DockInsertParam(Qt::Vertical, false);
    }
}

/**
 * Initializes the visible dock pannel count variable if it is not initialized yet
 */
void DockContainerPrivate::initVisibleDockPannelCount()
{
    if (m_visibleDockPannelCount > -1) {
        return;
    }
    m_visibleDockPannelCount = 0;

    for (auto pannel : qAsConst(m_dockPannels)) {
        m_visibleDockPannelCount += pannel->isHidden() ? 0 : 1;
    }
}

/**
 * Access function for the visible dock pannel counter
 */
int DockContainerPrivate::visibleDockPannelCount()
{
    // Lazy initialization - we initialize the m_visibleDockPannelCount variable
    // on first use
    initVisibleDockPannelCount();
    return m_visibleDockPannelCount;
}

/**
 * The visible dock pannel count changes, if dock pannels are remove, added or
 * when its view is toggled
 */
void DockContainerPrivate::onVisibleDockPannelCountChanged()
{
    auto topLevelDockPannel = q->topLevelDockPannel();

    if (topLevelDockPannel) {
        this->m_topLevelDockPannel = topLevelDockPannel;
        topLevelDockPannel->titleBarButton(UndockButton)->setVisible(false || !q->isFloating());
        topLevelDockPannel->titleBarButton(CloseButton)->setVisible(false || !q->isFloating());
    } else if (this->m_topLevelDockPannel) {
        this->m_topLevelDockPannel->titleBarButton(UndockButton)->setVisible(true);
        this->m_topLevelDockPannel->titleBarButton(CloseButton)->setVisible(true);
        this->m_topLevelDockPannel = nullptr;
    }
}

void DockContainerPrivate::onDockPannelViewToggled(bool visible)
{
    DockPannel *pannel = qobject_cast<DockPannel *>(q->sender());
    m_visibleDockPannelCount += visible ? 1 : -1;
    onVisibleDockPannelCountChanged();
    emit q->dockPannelViewToggled(pannel, visible);
}

void DockContainerPrivate::emitDockPannelsRemoved()
{
    onVisibleDockPannelCountChanged();
    emit q->dockPannelsRemoved();
}

void DockContainerPrivate::emitDockPannelsAdded()
{
    onVisibleDockPannelCountChanged();
    emit q->dockPannelsAdded();
}

DockSplitter *DockContainerPrivate::createSplitter(Qt::Orientation orientation, QWidget *parent)
{
    auto *splitter = new DockSplitter(orientation, parent);
    splitter->setOpaqueResize(QAds::testConfigFlag(QAds::OpaqueSplitterResize));
    splitter->setChildrenCollapsible(false);
    return splitter;
}

/**
 * Ensures equal distribution of the sizes of a splitter if an dock widget is inserted from code
 */
void DockContainerPrivate::adjustSplitterSizesOnInsertion(QSplitter *splitter, qreal lastRatio)
{
    const int areaSize = (splitter->orientation() == Qt::Horizontal) ? splitter->width()
                                                                     : splitter->height();
    auto splitterSizes = splitter->sizes();
    const qreal totalRatio = splitterSizes.size() - 1.0 + lastRatio;

    for (int i = 0; i < splitterSizes.size() - 1; ++i) {
        splitterSizes[i] = areaSize / totalRatio;
    }

    splitterSizes.back() = areaSize * lastRatio / totalRatio;
    splitter->setSizes(splitterSizes);
}

/**
 * This function forces the dock container widget to update handles of splitters
 * based if a central widget exists.
 */
void DockContainerPrivate::updateSplitterHandles(QSplitter *splitter)
{
    if (!m_dockManager->centralWidget() || !splitter) {
        return;
    }

    for (int i = 0; i < splitter->count(); ++i) {
        splitter->setStretchFactor(i, widgetResizesWithContainer(splitter->widget(i)) ? 1 : 0);
    }
}

/**
 * If no central widget exists, the widgets resize with the container.
 * If a central widget exists, the widgets surrounding the central widget
 * do not resize its height or width.
 */
bool DockContainerPrivate::widgetResizesWithContainer(QWidget *widget)
{
    if (!m_dockManager->centralWidget()) {
        return true;
    }

    auto pannel = qobject_cast<DockPannel *>(widget);
    if (pannel) {
        return pannel->isCentralPannel();
    }

    auto innerSplitter = qobject_cast<DockSplitter *>(widget);
    if (innerSplitter) {
        return innerSplitter->isResizingWithContainer();
    }

    return false;
}

void DockContainerPrivate::dropIntoContainer(DockFloatingWidget *floatingWidget, DockWidgetArea area)
{
    auto insertParam = dockAreaInsertParameters(area);
    DockContainer *floatingDockContainer = floatingWidget->dockContainer();
    auto newDockPannels = floatingDockContainer->findChildren<DockPannel *>(QString(), Qt::FindChildrenRecursively);
    QSplitter *splitter = m_rootSplitter;

    if (m_dockPannels.count() <= 1) {
        splitter->setOrientation(insertParam.orientation());
    } else if (splitter->orientation() != insertParam.orientation()) {
        QSplitter *newSplitter = createSplitter(insertParam.orientation());
        QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
        newSplitter->addWidget(splitter);
        updateSplitterHandles(newSplitter);
        splitter = newSplitter;
        delete layoutItem;
    }

    // Now we can insert the floating widget content into this container
    auto floatingSplitter = floatingDockContainer->rootSplitter();
    if (floatingSplitter->count() == 1) {
        insertWidgetIntoSplitter(splitter, floatingSplitter->widget(0), insertParam.append());
        updateSplitterHandles(splitter);
    } else if (floatingSplitter->orientation() == insertParam.orientation()) {
        int insertIndex = insertParam.append() ? splitter->count() : 0;

        while (floatingSplitter->count()) {
            splitter->insertWidget(insertIndex++, floatingSplitter->widget(0));
            updateSplitterHandles(splitter);
        }
    } else {
        insertWidgetIntoSplitter(splitter, floatingSplitter, insertParam.append());
    }

    m_rootSplitter = splitter;
    addDockPannels(newDockPannels);

    // If we dropped the floating widget into the main dock container that does
    // not contain any dock widgets, then splitter is invisible and we need to
    // show it to display the docked widgets
    if (!splitter->isVisible()) {
        splitter->show();
    }

    q->dumpLayout();
}

void DockContainerPrivate::dropIntoCenterOfSection(DockFloatingWidget *floatingWidget,
                                                   DockPannel *targetPannel)
{
    DockContainer *floatingContainer = floatingWidget->dockContainer();
    auto newDockWidgets = floatingContainer->dockWidgets();
    auto topLevelDockPannel = floatingContainer->topLevelDockPannel();
    int newCurrentIndex = -1;

    // If the floating widget contains only one single dock are, then the
    // current dock widget of the dock area will also be the future current
    // dock widget in the drop area.
    if (topLevelDockPannel) {
        newCurrentIndex = topLevelDockPannel->currentIndex();
    }

    for (int i = 0; i < newDockWidgets.count(); ++i) {
        DockWidget *dockWidget = newDockWidgets[i];
        targetPannel->insertDockWidget(i, dockWidget, false);

        // If the floating widget contains multiple visible dock areas, then we
        // simply pick the first visible open dock widget and make it
        // the current one.
        if (newCurrentIndex < 0 && !dockWidget->isClosed()) {
            newCurrentIndex = i;
        }
    }

    targetPannel->setCurrentIndex(newCurrentIndex);
    targetPannel->updateTitleBarVisibility();
    return;
}

void DockContainerPrivate::dropIntoSection(DockFloatingWidget *floatingWidget,
                                           DockPannel *targetPannel, DockWidgetArea area)
{
    // Dropping into center means all dock widgets in the dropped floating
    // widget will become tabs of the drop area
    if (area == CenterDockWidgetArea) {
        dropIntoCenterOfSection(floatingWidget, targetPannel);
        return;
    }

    auto container = floatingWidget->dockContainer();
    auto insertParam = dockAreaInsertParameters(area);
    auto newDockPannels = container->findChildren<DockPannel *>(QString(), Qt::FindChildrenRecursively);
    QSplitter *targetPannelSplitter = internal::findParent<QSplitter *>(targetPannel);

    if (!targetPannelSplitter) {
        QSplitter *splitter = createSplitter(insertParam.orientation());
        m_layout->replaceWidget(targetPannel, splitter);
        splitter->addWidget(targetPannel);
        updateSplitterHandles(splitter);
        targetPannelSplitter = splitter;
    }

    int index = targetPannelSplitter->indexOf(targetPannel);
    auto widget = container->findChild<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
    auto floatingSplitter = qobject_cast<QSplitter *>(widget);

    if (targetPannelSplitter->orientation() == insertParam.orientation()) {
        auto sizes = targetPannelSplitter->sizes();
        int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetPannel->width()
                                                                           : targetPannel->height();
        bool adjustSplitterSizes = true;

        if ((floatingSplitter->orientation() != insertParam.orientation()) && floatingSplitter->count() > 1) {
            targetPannelSplitter->insertWidget(index + insertParam.insertOffset(), widget);
            updateSplitterHandles(targetPannelSplitter);
        } else {
            adjustSplitterSizes = (floatingSplitter->count() == 1);
            int insertIndex = index + insertParam.insertOffset();

            while (floatingSplitter->count()) {
                targetPannelSplitter->insertWidget(insertIndex++, floatingSplitter->widget(0));
                updateSplitterHandles(targetPannelSplitter);
            }
        }

        if (adjustSplitterSizes) {
            int size = (targetAreaSize - targetPannelSplitter->handleWidth()) / 2;
            sizes[index] = size;
            sizes.insert(index, size);
            targetPannelSplitter->setSizes(sizes);
        }
    } else {
        QSplitter *newSplitter = createSplitter(insertParam.orientation());
        int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetPannel->width()
                                                                           : targetPannel->height();
        bool adjustSplitterSizes = true;

        if ((floatingSplitter->orientation() != insertParam.orientation()) && floatingSplitter->count() > 1) {
            newSplitter->addWidget(widget);
            updateSplitterHandles(newSplitter);
        } else {
            adjustSplitterSizes = (floatingSplitter->count() == 1);

            while (floatingSplitter->count()) {
                newSplitter->addWidget(floatingSplitter->widget(0));
                updateSplitterHandles(newSplitter);
            }
        }

        // Save the sizes before insertion and restore it later to prevent
        // shrinking of existing area
        auto sizes = targetPannelSplitter->sizes();
        insertWidgetIntoSplitter(newSplitter, targetPannel, !insertParam.append());
        updateSplitterHandles(newSplitter);
        if (adjustSplitterSizes) {
            int size = targetAreaSize / 2;
            newSplitter->setSizes({size, size});
        }

        targetPannelSplitter->insertWidget(index, newSplitter);
        targetPannelSplitter->setSizes(sizes);
        updateSplitterHandles(targetPannelSplitter);
    }

    addDockPannels(newDockPannels);
    q->dumpLayout();
}

void DockContainerPrivate::moveIntoCenterOfSection(QWidget *widget, DockPannel *targetPannel)
{
    auto droppedDockWidget = qobject_cast<DockWidget *>(widget);
    auto droppedPannel = qobject_cast<DockPannel *>(widget);

    if (droppedDockWidget) {
        DockPannel *oldPannel = droppedDockWidget->dockPannel();
        if (oldPannel == targetPannel) {
            return;
        }
        if (oldPannel) {
            oldPannel->removeDockWidget(droppedDockWidget);
        }
        targetPannel->insertDockWidget(0, droppedDockWidget, true);
    } else {
        QList<DockWidget *> newDockWidgets = droppedPannel->dockWidgets();
        int newCurrentIndex = droppedPannel->currentIndex();

        for (int i = 0; i < newDockWidgets.count(); ++i) {
            DockWidget *dockWidget = newDockWidgets[i];
            targetPannel->insertDockWidget(i, dockWidget, false);
        }

        targetPannel->setCurrentIndex(newCurrentIndex);
        droppedPannel->dockContainer()->removeDockPannel(droppedPannel);
        droppedPannel->deleteLater();
    }

    targetPannel->updateTitleBarVisibility();
    return;
}

void DockContainerPrivate::moveToNewSection(QWidget *widget, DockPannel *targetPannel,
                                            DockWidgetArea area)
{
    // Dropping into center means all dock widgets in the dropped floating
    // widget will become tabs of the drop area
    if (area == CenterDockWidgetArea) {
        moveIntoCenterOfSection(widget, targetPannel);
        return;
    }

    DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
    DockPannel *droppedDockPannel = qobject_cast<DockPannel *>(widget);
    DockPannel *newDockPannel;

    if (droppedDockWidget) {
        newDockPannel = new DockPannel(m_dockManager, q);
        DockPannel *oldPannel = droppedDockWidget->dockPannel();
        if (oldPannel) {
            oldPannel->removeDockWidget(droppedDockWidget);
        }
        newDockPannel->addDockWidget(droppedDockWidget);
    } else {
        droppedDockPannel->dockContainer()->removeDockPannel(droppedDockPannel);
        newDockPannel = droppedDockPannel;
    }

    auto insertParam = dockAreaInsertParameters(area);
    QSplitter *targetPannelSplitter = internal::findParent<QSplitter *>(targetPannel);
    const int index = targetPannelSplitter->indexOf(targetPannel);
    auto sizes = targetPannelSplitter->sizes();

    if (targetPannelSplitter->orientation() == insertParam.orientation()) {
        const int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetPannel->width()
                                                                                 : targetPannel->height();
        targetPannelSplitter->insertWidget(index + insertParam.insertOffset(), newDockPannel);
        updateSplitterHandles(targetPannelSplitter);
        const int size = (targetAreaSize - targetPannelSplitter->handleWidth()) / 2;
        sizes[index] = size;
        sizes.insert(index, size);
    } else {
        const int targetAreaSize = (insertParam.orientation() == Qt::Horizontal) ? targetPannel->width()
                                                                                 : targetPannel->height();
        QSplitter *newSplitter = createSplitter(insertParam.orientation());
        newSplitter->addWidget(targetPannel);
        insertWidgetIntoSplitter(newSplitter, newDockPannel, insertParam.append());
        updateSplitterHandles(newSplitter);
        const int size = targetAreaSize / 2;
        newSplitter->setSizes({size, size});
        targetPannelSplitter->insertWidget(index, newSplitter);
        updateSplitterHandles(targetPannelSplitter);
    }

    targetPannelSplitter->setSizes(sizes);
    addDockPannels({newDockPannel});
}

void DockContainerPrivate::moveToContainer(QWidget *widget, DockWidgetArea area)
{
    DockWidget *droppedDockWidget = qobject_cast<DockWidget *>(widget);
    DockPannel *droppedDockPannel = qobject_cast<DockPannel *>(widget);
    DockPannel *newDockPannel = nullptr;

    if (droppedDockWidget) {
        newDockPannel = new DockPannel(m_dockManager, q);
        DockPannel *oldPannel = droppedDockWidget->dockPannel();
        if (oldPannel) {
            oldPannel->removeDockWidget(droppedDockWidget);
        }
        newDockPannel->addDockWidget(droppedDockWidget);
    } else {
        // We check, if we insert the dropped widget into the same place that
        // it already has and do nothing, if it is the same place. It would
        // also work without this check, but it looks nicer with the check
        // because there will be no layout updates
        auto splitter = internal::findParent<DockSplitter *>(droppedDockPannel);
        auto insertParam = dockAreaInsertParameters(area);

        if (splitter == m_rootSplitter && insertParam.orientation() == splitter->orientation()) {
            if (insertParam.append() && splitter->lastWidget() == droppedDockPannel) {
                return;
            } else if (!insertParam.append() && splitter->firstWidget() == droppedDockPannel) {
                return;
            }
        }

        droppedDockPannel->dockContainer()->removeDockPannel(droppedDockPannel);
        newDockPannel = droppedDockPannel;
    }

    addDockPannel(newDockPannel, area);
    m_lastAddedPannelCache[areaToIndex(area)] = newDockPannel;
}

void DockContainerPrivate::addDockPannels(const QList<DockPannel *> newDockPannels)
{
    const int oldCount = m_dockPannels.count();
    const int newCount = newDockPannels.count();
    appendDockPannels(newDockPannels);

    // If the user dropped a floating widget that contains only one single
    // visible dock pannel, then its title bar button UndockButton is
    // likely hidden. We need to ensure, that it is visible
    for (auto pannel : qAsConst(newDockPannels)) {
        pannel->titleBarButton(UndockButton)->setVisible(true);
        pannel->titleBarButton(CloseButton)->setVisible(true);
    }

    // We need to ensure, that the dock pannel title bar is visible. The title bar
    // is invisible, if the dock are is a single dock pannel in a floating widget.
    if (oldCount == 1) {
        m_dockPannels.at(0)->updateTitleBarVisibility();
    }
    if (newCount == 1) {
        m_dockPannels.last()->updateTitleBarVisibility();
    }

    emitDockPannelsAdded();
}

void DockContainerPrivate::appendDockPannels(const QList<DockPannel *> newDockPannels)
{
    m_dockPannels.append(newDockPannels);

    for (auto pannel : qAsConst(newDockPannels)) {
        QObject::connect(pannel, &DockPannel::viewToggled,
                         q, std::bind(&DockContainerPrivate::onDockPannelViewToggled,
                                      this, std::placeholders::_1));
    }
}

void DockContainerPrivate::saveChildNodesState(QXmlStreamWriter &stream, QWidget *widget)
{
    QSplitter *splitter = qobject_cast<QSplitter *>(widget);

    if (splitter) {
        stream.writeStartElement("splitter");
        stream.writeAttribute("orientation", QVariant::fromValue(splitter->orientation()).toString());
        stream.writeAttribute("count", QString::number(splitter->count()));

        for (int i = 0; i < splitter->count(); ++i) {
            saveChildNodesState(stream, splitter->widget(i));
        }

        stream.writeStartElement("sizes");
        QStringList sizes;
        for (auto size : splitter->sizes()) {
            sizes.append(QString::number(size));
        }

        stream.writeCharacters(sizes.join(" "));
        stream.writeEndElement(); // sizes
        stream.writeEndElement(); // splitter
    } else {
        DockPannel *pannel = qobject_cast<DockPannel *>(widget);
        if (pannel) {
            pannel->saveState(stream);
        }
    }
}

bool DockContainerPrivate::restoreSplitter(DockStateReader &stateReader, QWidget *&createdWidget, bool testing)
{
    QVariant orientationVar = QVariant(stateReader.attributes().value("orientation").toString());

    // Check if the orientation string is convertable
    if (!orientationVar.canConvert<Qt::Orientation>()) {
        return false;
    }

    Qt::Orientation orientation = orientationVar.value<Qt::Orientation>();
    bool ok;
    int widgetCount = stateReader.attributes().value("count").toInt(&ok);
    if (!ok) {
        return false;
    }

    QSplitter *splitter = nullptr;
    if (!testing) {
        splitter = createSplitter(orientation);
    }

    bool visible = false;
    QList<int> sizes;

    while (stateReader.readNextStartElement()) {
        QWidget *childNode = nullptr;
        bool result = true;

        if (stateReader.name() == QLatin1String("splitter")) {
            result = restoreSplitter(stateReader, childNode, testing);
        } else if (stateReader.name() == QLatin1String("pannel")) {
            result = restoreDockPannel(stateReader, childNode, testing);
        } else if (stateReader.name() == QLatin1String("sizes")) {
            QString size = stateReader.readElementText().trimmed();
            QTextStream textStream(&size);
            while (!textStream.atEnd()) {
                int value;
                textStream >> value;
                sizes.append(value);
            }
        } else {
            stateReader.skipCurrentElement();
        }

        if (!result) {
            return false;
        }
        if (testing || !childNode) {
            continue;
        }

        splitter->addWidget(childNode);
        visible |= childNode->isVisibleTo(splitter);
    }
    if(!testing) {
       updateSplitterHandles(splitter);
    }
    if (sizes.count() != widgetCount) {
        return false;
    }
    if (!testing) {
        if (!splitter->count()) {
            delete splitter;
            splitter = nullptr;
        } else {
            splitter->setSizes(sizes);
            splitter->setVisible(visible);
        }
        createdWidget = splitter;
    } else {
        createdWidget = nullptr;
    }

    return true;
}

bool DockContainerPrivate::restoreDockPannel(DockStateReader &stateReader,
                                           QWidget *&createdWidget, bool testing)
{
#ifdef QADS_DEBUG_PRINT
    bool ok;
    int tabs = stateReader.attributes().value("tabs").toInt(&ok);
    if (!ok) {
        return false;
    }
#endif

    QString currentDockWidget = stateReader.attributes().value("current").toString();
    DockPannel *pannel = nullptr;
    if (!testing) {
        pannel = new DockPannel(m_dockManager, q);
        const auto allowedAreasAttribute = stateReader.attributes().value("allowedAreas");
        if (!allowedAreasAttribute.isEmpty()) {
            pannel->setAllowedAreas((DockWidgetArea)allowedAreasAttribute.toInt(nullptr, 16));
        }

        const auto flagsAttribute = stateReader.attributes().value("flags");
        if (!flagsAttribute.isEmpty()) {
            pannel->setDockPannelFlags((DockPannel::DockPannelFlags)flagsAttribute.toInt(nullptr, 16));
        }
    }

    while (stateReader.readNextStartElement()) {
        if (stateReader.name() != QLatin1String("widget")) {
            continue;
        }
        auto objectName = stateReader.attributes().value("name");
        if (objectName.isEmpty()) {
            return false;
        }

        QVariant closedVar = QVariant(stateReader.attributes().value("closed").toString());
        if (!closedVar.canConvert<bool>()) {
            return false;
        }

        bool closed = closedVar.value<bool>();
        stateReader.skipCurrentElement();
        DockWidget *dockWidget = m_dockManager->findDockWidget(objectName.toString());
        if (!dockWidget || testing) {
            continue;
        }

        // We hide the DockArea here to prevent the short display (the flashing)
        // of the dock areas during application startup
        pannel->hide();
        pannel->addDockWidget(dockWidget);
        dockWidget->setToggleViewActionChecked(!closed);
        dockWidget->setClosedState(closed);
        dockWidget->setProperty(internal::closedProperty, closed);
        dockWidget->setProperty(internal::dirtyProperty, false);
    }
    if (testing) {
        return true;
    }
    if (!pannel->dockWidgetsCount()) {
        delete pannel;
        pannel = nullptr;
    } else {
        pannel->setProperty("currentDockWidget", currentDockWidget);
        appendDockPannels( {pannel});
    }

    createdWidget = pannel;
    return true;
}

bool DockContainerPrivate::restoreChildNodes(DockStateReader &stateReader,
                                             QWidget *&createdWidget, bool testing)
{
    bool result = true;

    while (stateReader.readNextStartElement()) {
        if (stateReader.name() == QLatin1String("splitter")) {
            result = restoreSplitter(stateReader, createdWidget, testing);
        } else if (stateReader.name() == QLatin1String("pannel")) {
            result = restoreDockPannel(stateReader, createdWidget, testing);
        } else {
            stateReader.skipCurrentElement();
        }
    }

    return result;
}

DockPannel *DockContainerPrivate::addDockWidget(DockWidgetArea area, DockWidget *dockWidget)
{
    DockPannel *newPannel = new DockPannel(m_dockManager, q);
    newPannel->addDockWidget(dockWidget);
    addDockPannel(newPannel, area);
    newPannel->updateTitleBarVisibility();
    m_lastAddedPannelCache[areaToIndex(area)] = newPannel;
    return newPannel;
}

void DockContainerPrivate::addDockPannel(DockPannel *newPannel, DockWidgetArea area)
{
    auto insertParam = dockAreaInsertParameters(area);

    // As long as we have only one dock area in the splitter we can adjust its orientation
    if (m_dockPannels.count() <= 1) {
        m_rootSplitter->setOrientation(insertParam.orientation());
    }

    QSplitter *splitter = m_rootSplitter;

    if (splitter->orientation() == insertParam.orientation()) {
        insertWidgetIntoSplitter(splitter, newPannel, insertParam.append());
        updateSplitterHandles(splitter);
        if (splitter->isHidden()) {
            splitter->show();
        }
    } else {
        QSplitter *newSplitter = createSplitter(insertParam.orientation());
        if (insertParam.append()) {
            QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
            newSplitter->addWidget(splitter);
            newSplitter->addWidget(newPannel);
            updateSplitterHandles(newSplitter);
            delete layoutItem;
        } else {
            newSplitter->addWidget(newPannel);
            QLayoutItem *layoutItem = m_layout->replaceWidget(splitter, newSplitter);
            newSplitter->addWidget(splitter);
            updateSplitterHandles(newSplitter);
            delete layoutItem;
        }
        m_rootSplitter = newSplitter;
    }

    addDockPannels({newPannel});
}

void DockContainerPrivate::dumpRecursive(int level, QWidget *widget) const
{
#if defined(QT_DEBUG)
    QSplitter *splitter = qobject_cast<QSplitter *>(widget);
    QByteArray buf;
    buf.fill(' ', level * 4);

    if (splitter) {
#ifdef QADS_DEBUG_PRINT
        qDebug("%sSplitter %s v: %s c: %s",
               buf.data(),
               (splitter->orientation() == Qt::Vertical) ? "--" : "|",
               splitter->isHidden() ? " " : "v",
               QString::number(splitter->count()).toStdString().c_str());
        std::cout << buf.data() << "Splitter "
                  << ((splitter->orientation() == Qt::Vertical) ? "--" : "|") << " "
                  << (splitter->isHidden() ? " " : "v") << " "
                  << QString::number(splitter->count()).toStdString() << std::endl;
#endif
        for (int i = 0; i < splitter->count(); ++i) {
            dumpRecursive(level + 1, splitter->widget(i));
        }
    } else {
        DockPannel *pannel = qobject_cast<DockPannel *>(widget);
        if (!pannel) {
            return;
        }

#ifdef QADS_DEBUG_PRINT
        qDebug("%sDockArea", buf.data());
        std::cout << buf.data() << (pannel->isHidden() ? " " : "v")
                  << (pannel->openDockWidgetsCount() > 0 ? " " : "c") << " DockArea"
                  << std::endl;
        buf.fill(' ', (level + 1) * 4);

        for (int i = 0; i < pannel->dockWidgetsCount(); ++i) {
            std::cout << buf.data() << (i == pannel->currentIndex() ? "*" : " ");
            DockWidget *dockWidget = pannel->dockWidget(i);
            std::cout << (dockWidget->isHidden() ? " " : "v");
            std::cout << (dockWidget->isClosed() ? "c" : " ") << " ";
            std::cout << dockWidget->windowTitle().toStdString() << std::endl;
        }
#endif
    }

#else
    Q_UNUSED(level)
    Q_UNUSED(widget)
#endif
}

DockPannel *DockContainerPrivate::addDockWidget(DockWidgetArea area, DockWidget *dockWidget,
                                                DockPannel *targetPannel)
{
    if (area == CenterDockWidgetArea) {
        targetPannel->addDockWidget(dockWidget);
        targetPannel->updateTitleBarVisibility();
        return targetPannel;
    }

    DockPannel *newDockPannel = new DockPannel(m_dockManager, q);
    newDockPannel->addDockWidget(dockWidget);
    auto insertParam = dockAreaInsertParameters(area);
    QSplitter *targetPannelSplitter = internal::findParent<QSplitter *>(targetPannel);
    Q_ASSERT(targetPannelSplitter != nullptr);
    int index = targetPannelSplitter->indexOf(targetPannel);

    if (targetPannelSplitter->orientation() == insertParam.orientation()) {
        targetPannelSplitter->insertWidget(index + insertParam.insertOffset(), newDockPannel);
        updateSplitterHandles(targetPannelSplitter);
        // do nothing, if flag is not enabled
        if (QAds::testConfigFlag(QAds::EqualSplitOnInsertion)) {
            adjustSplitterSizesOnInsertion(targetPannelSplitter);
        }
    } else {
        auto targetAreaSizes = targetPannelSplitter->sizes();
        QSplitter *newSplitter = createSplitter(insertParam.orientation());
        newSplitter->addWidget(targetPannel);
        insertWidgetIntoSplitter(newSplitter, newDockPannel, insertParam.append());
        updateSplitterHandles(newSplitter);
        targetPannelSplitter->insertWidget(index, newSplitter);
        updateSplitterHandles(targetPannelSplitter);
        if (QAds::testConfigFlag(QAds::EqualSplitOnInsertion)) {
            targetPannelSplitter->setSizes(targetAreaSizes);
            adjustSplitterSizesOnInsertion(newSplitter);
        }
    }

    appendDockPannels({newDockPannel});
    emitDockPannelsAdded();
    return newDockPannel;
}

DockContainer::DockContainer(DockManager *dockManager, QWidget *parent)
    : QWidget(parent)
    , d(new DockContainerPrivate(this))
{
    setAttribute(Qt::WA_StyledBackground, true);
    d->m_dockManager = dockManager;
    d->m_isFloating = floatingWidget() != nullptr;
    d->m_layout = new QGridLayout();
    d->m_layout->setContentsMargins(0, 0, 0, 0);
    d->m_layout->setSpacing(0);
    setLayout(d->m_layout);

    // The function d->createSplitter() accesses the config flags from dock
    // manager which in turn requires a properly constructed dock manager.
    // If this dock container is the dock manager, then it is not properly
    // constructed yet because this base class constructor is called before
    // the constructor of the DockManager private class
    if (dockManager != this) {
        d->m_dockManager->registerDockContainer(this);
        createRootSplitter();
    }
}

DockContainer::~DockContainer()
{
    if (d->m_dockManager) {
        d->m_dockManager->removeDockContainer(this);
    }

    delete d;
}

DockPannel *DockContainer::addDockWidget(DockWidgetArea area, DockWidget *dockWidget, DockPannel *pannel)
{
    DockPannel *oldPannel = dockWidget->dockPannel();
    if (oldPannel) {
        oldPannel->removeDockWidget(dockWidget);
    }

    dockWidget->setDockManager(d->m_dockManager);

    if (pannel) {
        return d->addDockWidget(area, dockWidget, pannel);
    } else {
        return d->addDockWidget(area, dockWidget);
    }
}

void DockContainer::removeDockWidget(DockWidget *dockWidget)
{
    DockPannel *pannel = dockWidget->dockPannel();
    if (pannel) {
        pannel->removeDockWidget(dockWidget);
    }
}

bool DockContainer::hasTopLevelDockWidget() const
{
    auto pannels = openedDockPannels();
    if (pannels.count() != 1) {
        return false;
    }
    return pannels[0]->openDockWidgetsCount() == 1;
}

QList<DockWidget *> DockContainer::openedDockWidgets() const
{
    QList<DockWidget *> dockWidgetList;
    for (auto pannel : d->m_dockPannels) {
        if (!pannel->isHidden()) {
            dockWidgetList.append(pannel->openedDockWidgets());
        }
    }
    return dockWidgetList;
}

DockPannel *DockContainer::dockPannelAt(const QPoint &globalPos) const
{
    for (auto pannel : qAsConst(d->m_dockPannels)) {
        if (pannel->isVisible()
                && pannel->rect().contains(pannel->mapFromGlobal(globalPos))) {
            return pannel;
        }
    }
    return nullptr;
}

DockPannel *DockContainer::dockPannel(int index) const
{
    return (index < dockPannelCount()) ? d->m_dockPannels[index] : nullptr;
}

QList<DockPannel *> DockContainer::openedDockPannels() const
{
    QList<DockPannel *> result;
    for (auto pannel : qAsConst(d->m_dockPannels)) {
        if (!pannel->isHidden()) {
            result.append(pannel);
        }
    }
    return result;
}

/**
 * This function returns true, if the container has open dock areas.
 * This functions is a little bit faster than calling openedDockAreas().isEmpty()
 * because it returns as soon as it finds an open dock area
 */
bool DockContainer::hasOpenDockPannels() const
{
    for (auto pannel : d->m_dockPannels) {
        if (!pannel->isHidden()) {
            return true;
        }
    }
    return false;
}

int DockContainer::dockPannelCount() const
{
    return d->m_dockPannels.count();
}

int DockContainer::visibleDockPannelCount() const
{
    int result = 0;
    for (auto pannel : qAsConst(d->m_dockPannels)) {
        result += pannel->isHidden() ? 0 : 1;
    }
    return result;
    // TODO Cache or precalculate this to speed it up because it is used during
    // movement of floating widget
    //return d->visibleDockPannelCount();
}

void DockContainer::closeOtherPannels(DockPannel *keepOpenPannel)
{
    for (const auto pannel : d->m_dockPannels) {
        if (pannel == keepOpenPannel) {
            continue;
        }
        if (!pannel->features(BitwiseAnd).testFlag(DockWidget::DockWidgetClosable)) {
            continue;
        }
        // We do not close areas with widgets with custom close handling
        if (pannel->features(BitwiseOr).testFlag(DockWidget::CustomCloseHandling)) {
            continue;
        }
        pannel->closePannel();
    }
}

DockWidget::DockWidgetFeatures DockContainer::features() const
{
    DockWidget::DockWidgetFeatures features(DockWidget::AllDockWidgetFeatures);
    for (const auto pannel : d->m_dockPannels) {
        features &= pannel->features();
    }
    return features;
}

DockFloatingWidget *DockContainer::floatingWidget() const
{
    return internal::findParent<DockFloatingWidget *>(this);
}

bool DockContainer::isFloating() const
{
    return d->m_isFloating;
}

unsigned int DockContainer::zOrderIndex() const
{
    return d->m_zOrderIndex;
}

bool DockContainer::isFrontOf(DockContainer *other) const
{
    return this->zOrderIndex() > other->zOrderIndex();
}

void DockContainer::dumpLayout() const
{
#if (QADS_DEBUG_LEVEL > 0)
    qDebug("\n\nDumping layout --------------------------");
    std::cout << "\n\nDumping layout --------------------------" << std::endl;
    d->dumpRecursive(0, d->m_rootSplitter);
    qDebug("--------------------------\n\n");
    std::cout << "--------------------------\n\n" << std::endl;
#endif
}

QSplitter *DockContainer::rootSplitter() const
{
    return d->m_rootSplitter;
}

void DockContainer::createRootSplitter()
{
    if (d->m_rootSplitter) {
        return;
    }

    d->m_rootSplitter = d->createSplitter(Qt::Horizontal);
    d->m_layout->addWidget(d->m_rootSplitter);
}

/**
 * This function forces the dock container widget to update handles of splitters
 * based on resize modes of dock widgets contained in the container.
 */
void DockContainer::updateSplitterHandles(QSplitter *splitter)
{
    d->updateSplitterHandles(splitter);
}

DockPannel *DockContainer::lastAddedDockPannel(DockWidgetArea area) const
{
    return d->m_lastAddedPannelCache[areaToIndex(area)];
}

DockPannel *DockContainer::topLevelDockPannel() const
{
    auto pannels = openedDockPannels();
    if (pannels.count() != 1) {
        return nullptr;
    }
    return pannels[0];
}

DockWidget *DockContainer::topLevelDockWidget() const
{
    auto pannel = topLevelDockPannel();
    if (!pannel) {
        return nullptr;
    }

    auto dockWidgets = pannel->openedDockWidgets();
    if (dockWidgets.count() != 1) {
        return nullptr;
    }

    return dockWidgets[0];
}

QList<DockWidget *> DockContainer::dockWidgets() const
{
    QList<DockWidget *> result;
    for (const auto pannel : qAsConst(d->m_dockPannels)) {
        result.append(pannel->dockWidgets());
    }
    return result;
}

void DockContainer::dropFloatingWidget(DockFloatingWidget *floatingWidget, const QPoint &targetPosition)
{
    DockWidget *singleDroppedDockWidget = floatingWidget->topLevelDockWidget();
    DockWidget *singleDockWidget = topLevelDockWidget();
    DockPannel *pannel = dockPannelAt(targetPosition);
    auto dropArea = InvalidDockWidgetArea;
    auto containerDropArea = d->m_dockManager->containerOverlay()->dropAreaUnderCursor();
    bool dropped = false;

    if (pannel) {
        auto dropOverlay = d->m_dockManager->pannelOverlay();
        dropOverlay->setAllowedAreas(pannel->allowedAreas());
        dropArea = dropOverlay->showOverlay(pannel);

        if (containerDropArea != InvalidDockWidgetArea && containerDropArea != dropArea) {
            dropArea = InvalidDockWidgetArea;
        }
        if (dropArea != InvalidDockWidgetArea) {
            d->dropIntoSection(floatingWidget, pannel, dropArea);
            dropped = true;
        }
    }

    // mouse is over container
    if (InvalidDockWidgetArea == dropArea) {
        dropArea = containerDropArea;
        if (dropArea != InvalidDockWidgetArea) {
            d->dropIntoContainer(floatingWidget, dropArea);
            dropped = true;
        }
    }

    if (dropped) {
        // Fix https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System/issues/351
        floatingWidget->hideAndDeleteLater();
        // If we dropped a floating widget with only one single dock widget, then we
        // drop a top level widget that changes from floating to docked now
        DockWidget::emitTopLevelEventForWidget(singleDroppedDockWidget, false);
        // If there was a top level widget before the drop, then it is not top
        // level widget anymore
        DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);
    }

    window()->activateWindow();

    if (singleDroppedDockWidget) {
        d->m_dockManager->notifyWidgetOrPannelRelocation(singleDroppedDockWidget);
    }

    d->m_dockManager->notifyFloatingWidgetDrop(floatingWidget);
}

void DockContainer::dropWidget(QWidget *widget, DockWidgetArea dropArea, DockPannel *targetPannel)
{
    DockWidget *singleDockWidget = topLevelDockWidget();

    if (targetPannel) {
        d->moveToNewSection(widget, targetPannel, dropArea);
    } else {
        d->moveToContainer(widget, dropArea);
    }

    // If there was a top level widget before the drop, then it is not top
    // level widget anymore
    DockWidget::emitTopLevelEventForWidget(singleDockWidget, false);

    window()->activateWindow();
    d->m_dockManager->notifyWidgetOrPannelRelocation(widget);
}

void DockContainer::addDockPannel(DockPannel *pannel, DockWidgetArea area)
{
    DockContainer *container = pannel->dockContainer();
    if (container && container != this) {
        container->removeDockPannel(pannel);
    }
    d->addDockPannel(pannel, area);
}

void DockContainer::removeDockPannel(DockPannel *pannel)
{
    pannel->disconnect(this);
    d->m_dockPannels.removeAll(pannel);
    DockSplitter *splitter = internal::findParent<DockSplitter *>(pannel);
    // Remove area from parent splitter and recursively hide tree of parent
    // splitters if it has no visible content
    pannel->setParent(nullptr);
    internal::hideEmptyParentSplitters(splitter);
    // Remove this area from cached areas
    const auto &cache = d->m_lastAddedPannelCache;
    if (auto p = std::find(cache, cache + sizeof(cache) / sizeof(cache[0]), pannel)) {
        d->m_lastAddedPannelCache[std::distance(cache, p)] = nullptr;
    }

    // If splitter has more than 1 widgets, we are finished and can leave
    if (splitter->count() > 1) {
        updateSplitterHandles(splitter);
        emitAndExit();
        return;
    }

    // If this is the RootSplitter we need to remove empty splitters to
    // avoid too many empty splitters
    if (splitter == d->m_rootSplitter) {
        // If splitter is empty, we are finished
        if (!splitter->count()) {
            splitter->hide();
            emitAndExit();
            return;
        }

        QWidget *widget = splitter->widget(0);
        QSplitter *childSplitter = qobject_cast<QSplitter *>(widget);

        // If the one and only content widget of the splitter is not a splitter
        // then we are finished
        if (!childSplitter) {
            emitAndExit();
            return;
        }
        // We replace the superfluous RootSplitter with the ChildSplitter
        childSplitter->setParent(nullptr);
        QLayoutItem *layoutItem = d->m_layout->replaceWidget(splitter, childSplitter);
        d->m_rootSplitter = childSplitter;
        delete layoutItem;
    } else if (splitter->count() == 1) {
        QSplitter *parentSplitter = internal::findParent<QSplitter *>(splitter);
        auto sizes = parentSplitter->sizes();
        QWidget *widget = splitter->widget(0);
        widget->setParent(this);
        internal::replaceSplitterWidget(parentSplitter, splitter, widget);
        parentSplitter->setSizes(sizes);
    }

    delete splitter;
}

void DockContainer::emitAndExit() const
{
    DockWidget *topLevelWidget = topLevelDockWidget();
    // Updated the title bar visibility of the dock widget if there is only
    // one single visible dock widget
    DockWidget::emitTopLevelEventForWidget(topLevelWidget, true);
    dumpLayout();
    d->emitDockPannelsRemoved();
}

void DockContainer::saveState(QXmlStreamWriter &stream) const
{
    stream.writeStartElement("container");
    stream.writeAttribute("floating", QVariant::fromValue(isFloating()).toString());

    if (isFloating()) {
        DockFloatingWidget *floatingDockContainer = floatingWidget();
        QByteArray geometry = floatingDockContainer->saveGeometry();
        stream.writeTextElement("geometry", QString::fromLatin1(geometry.toBase64()));
    }

    d->saveChildNodesState(stream, d->m_rootSplitter);
    stream.writeEndElement();
}

bool DockContainer::restoreState(DockStateReader &stateReader, bool testing)
{
    QVariant floatingVar = QVariant(stateReader.attributes().value("floating").toString());
    if (!floatingVar.canConvert<bool>()) {
        return false;
    }

    bool isFloating = floatingVar.value<bool>();
    QWidget *newRootSplitter {};

    if (!testing) {
        d->m_visibleDockPannelCount = -1; // invalidate the dock area count
        d->m_dockPannels.clear();
        std::fill(std::begin(d->m_lastAddedPannelCache), std::end(d->m_lastAddedPannelCache), nullptr);
    }

    if (isFloating) {
        if (!stateReader.readNextStartElement() || stateReader.name() != QLatin1String("geometry")) {
            return false;
        }
        QByteArray geometryString =
                stateReader.readElementText(DockStateReader::ErrorOnUnexpectedElement).toLocal8Bit();
        QByteArray geometry = QByteArray::fromBase64(geometryString);
        if (geometry.isEmpty()) {
            return false;
        }

        if (!testing) {
            DockFloatingWidget *floatingDockContainer = floatingWidget();
            if (floatingDockContainer) {
                floatingDockContainer->restoreGeometry(geometry);
            }
        }
    }

    if (!d->restoreChildNodes(stateReader, newRootSplitter, testing)) {
        return false;
    }
    if (testing) {
        return true;
    }

    // If the root splitter is empty, rostoreChildNodes returns a 0 pointer
    // and we need to create a new empty root splitter
    if (!newRootSplitter) {
        newRootSplitter = d->createSplitter(Qt::Horizontal);
    }

    d->m_layout->replaceWidget(d->m_rootSplitter, newRootSplitter);
    QSplitter *oldRoot = d->m_rootSplitter;
    d->m_rootSplitter = qobject_cast<QSplitter *>(newRootSplitter);
    oldRoot->deleteLater();
    return true;
}

bool DockContainer::event(QEvent *event)
{
    bool result = QWidget::event(event);

    if (event->type() == QEvent::WindowActivate) {
        d->m_zOrderIndex = ++zOrderCounter;
    } else if (event->type() == QEvent::Show && !d->m_zOrderIndex) {
        d->m_zOrderIndex = ++zOrderCounter;
    }

    return result;
}

QADS_END_NAMESPACE
